#pragma once

#include "Arduino.h"
#include <Adafruit_I2CDevice.h>
#include "A_Sensor.h"

#define SHT40_DEFAULT_ADDR 0x44 /**< SHT40 I2C Address */
#define SHT40_NOHEAT_HIGHPRECISION                                             \
  0xFD /**< High precision measurement, no heater */
#define SHT40_NOHEAT_MEDPRECISION                                              \
  0xF6 /**< Medium precision measurement, no heater */
#define SHT40_NOHEAT_LOWPRECISION                                              \
  0xE0 /**< Low precision measurement, no heater */

#define SHT40_HIGHHEAT_1S                                                      \
  0x39 /**< High precision measurement, high heat for 1 sec */
#define SHT40_HIGHHEAT_100MS                                                   \
  0x32 /**< High precision measurement, high heat for 0.1 sec */
#define SHT40_MEDHEAT_1S                                                       \
  0x2F /**< High precision measurement, med heat for 1 sec */
#define SHT40_MEDHEAT_100MS                                                    \
  0x24 /**< High precision measurement, med heat for 0.1 sec */
#define SHT40_LOWHEAT_1S                                                       \
  0x1E /**< High precision measurement, low heat for 1 sec */
#define SHT40_LOWHEAT_100MS                                                    \
  0x15 /**< High precision measurement, low heat for 0.1 sec */

#define SHT40_READSERIAL 0x89 /**< Read Out of Serial Register */
#define SHT40_SOFTRESET 0x94  /**< Soft Reset */

/** How precise (repeatable) the measurement will be */
typedef enum {
    SHT40_HIGH_PRECISION,
    SHT40_MED_PRECISION,
    SHT40_LOW_PRECISION,
} SHT40_precision_t;

/** Optional pre-heater configuration setting */
typedef enum {
    SHT40_NO_HEATER,
    SHT40_HIGH_HEATER_1S,
    SHT40_HIGH_HEATER_100MS,
    SHT40_MED_HEATER_1S,
    SHT40_MED_HEATER_100MS,
    SHT40_LOW_HEATER_1S,
    SHT40_LOW_HEATER_100MS,
} SHT40_heater_t;

class SHT40;

/**
 * @brief  Adafruit Unified Sensor interface for the humidity sensor component
 * of SHT40
 *
 */
class SHT40_Humidity : public A_Sensor {
public:
    /** @brief Create an A_Sensor compatible object for the humidity sensor
      @param parent A pointer to the SHT40 class */
    SHT40_Humidity(SHT40 *parent) { _theSHT40 = parent; }
    bool getEvent(sensors_event_t *);
    void getSensor(sensor1_t *);

private:
    int _sensorID = 0x0401;
    SHT40 *_theSHT40 = NULL;
};

/**
 * @brief Adafruit Unified Sensor interface for the temperature sensor component
 * of SHT40
 *
 */
class SHT40_Temp : public A_Sensor {
public:
    /** @brief Create an A_Sensor compatible object for the temp sensor
        @param parent A pointer to the SHT40 class */
    SHT40_Temp(SHT40 *parent) { _theSHT40 = parent; }

    bool getEvent(sensors_event_t *);
    void getSensor(sensor1_t *);

private:
    int _sensorID = 0x0400;
    SHT40 *_theSHT40 = NULL;
};

/**
 * Driver for the Adafruit SHT40 Temperature and Humidity breakout board.
 */
class SHT40 {
public:
    SHT40(void);
    ~SHT40(void);

    bool begin(uint8_t SDAPin, uint8_t SCLPin,TwoWire *theWire = &Wire);
    uint32_t readSerial(void);
    bool reset(void);

    void setPrecision(SHT40_precision_t prec);
    SHT40_precision_t getPrecision(void);
    void setHeater(SHT40_heater_t heat);
    SHT40_heater_t getHeater(void);

    bool getEvent(sensors_event_t *humidity, sensors_event_t *temp);
    A_Sensor *getTemperatureSensor(void);
    A_Sensor *getHumiditySensor(void);

protected:
    float _temperature, ///< Last reading's temperature (C)
    _humidity;      ///< Last reading's humidity (percent)

    uint16_t _sensorid_humidity; ///< ID number for humidity
    uint16_t _sensorid_temp;     ///< ID number for temperature

    Adafruit_I2CDevice *i2c_dev = NULL;      ///< Pointer to I2C bus interface
    SHT40_Temp *temp_sensor = NULL; ///< Temp sensor data object
    SHT40_Humidity *humidity_sensor =
            NULL; ///< Humidity sensor data object

private:
    SHT40_precision_t _precision = SHT40_HIGH_PRECISION;
    SHT40_heater_t _heater = SHT40_NO_HEATER;

    bool writeCommand(uint16_t cmd);
    bool readCommand(uint16_t command, uint8_t *buffer, uint8_t num_bytes);

    friend class SHT40_Temp;     ///< Gives access to private members to
    ///< Temp data object
    friend class SHT40_Humidity; ///< Gives access to private members to
    ///< Humidity data object

    void fillTempEvent(sensors_event_t *temp);
    void fillHumidityEvent(sensors_event_t *humidity);
};
